#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Behavior Analysis Manager
Analyzes object behaviors in video frames and detects violations
"""

import logging
import time
import numpy as np
from typing import Dict, List, Tuple, Optional, Any

logger = logging.getLogger("video_processing.behavior_analyzer")

class BehaviorAnalyzer:
    """Behavior Analysis Manager Class"""
    
    def __init__(self):
        """Initialize behavior analyzer"""
        self.camera_configs = {}
        self.object_history = {}
        self.violation_counts = {}
        self.last_violation_time = {}
        logger.info("Behavior analyzer initialized")
    
    def set_camera_config(self, camera_id: str, config: Dict[str, Any]):
        """Set configuration for specific camera
        
        Args:
            camera_id: Camera identifier
            config: Camera-specific configuration
        """
        self.camera_configs[camera_id] = config
        logger.info(f"Set configuration for camera {camera_id}")
    
    def analyze_frame(self, camera_id: str, frame: np.ndarray, tracked_objects: List[Dict], detections: List[Dict]) -> Dict[str, Any]:
        """Analyze behaviors in current frame
        
        Args:
            camera_id: Camera identifier
            frame: Current frame
            tracked_objects: List of tracked objects
            detections: List of detected objects
            
        Returns:
            Dict containing analysis results and violations
        """
        try:
            # Initialize camera configuration if not exists
            if camera_id not in self.camera_configs:
                self.camera_configs[camera_id] = self._get_default_config()
            
            # Initialize object history if not exists
            if camera_id not in self.object_history:
                self.object_history[camera_id] = {}
            
            # Initialize violation counts if not exists
            if camera_id not in self.violation_counts:
                self.violation_counts[camera_id] = {}
            
            # Initialize last violation time if not exists
            if camera_id not in self.last_violation_time:
                self.last_violation_time[camera_id] = {}
            
            config = self.camera_configs[camera_id]
            results = {
                'tracked_objects': len(tracked_objects),
                'detections': len(detections),
                'violations': []
            }
            
            # Process each tracked object
            for obj in tracked_objects:
                obj_id = obj.get('track_id', 'unknown')
                class_name = obj.get('class_name', 'unknown')
                
                # Update object history
                self._update_object_history(camera_id, obj_id, obj)
                
                # Analyze behavior based on object class
                if class_name.lower() == 'person':
                    violations = self._analyze_person_behavior(camera_id, obj_id, obj, config)
                elif class_name.lower() == 'vehicle':
                    violations = self._analyze_vehicle_behavior(camera_id, obj_id, obj, config)
                else:
                    violations = self._analyze_generic_behavior(camera_id, obj_id, obj, config)
                
                # Add violations to results
                results['violations'].extend(violations)
            
            # Clean up old history data
            self._cleanup_old_data(camera_id)
            
            return results
            
        except Exception as e:
            logger.error(f"Error analyzing behaviors for camera {camera_id}: {str(e)}")
            return {
                'tracked_objects': len(tracked_objects),
                'detections': len(detections),
                'violations': []
            }
    
    def _update_object_history(self, camera_id: str, obj_id: str, obj: Dict):
        """Update object tracking history"""
        timestamp = time.time()
        
        if obj_id not in self.object_history[camera_id]:
            self.object_history[camera_id][obj_id] = []
        
        # Store position and timestamp
        position_data = {
            'timestamp': timestamp,
            'x1': obj.get('x1', 0),
            'y1': obj.get('y1', 0),
            'x2': obj.get('x2', 0),
            'y2': obj.get('y2', 0),
            'confidence': obj.get('confidence', 0),
            'class_name': obj.get('class_name', 'unknown')
        }
        
        # Keep only the last 30 frames of history
        self.object_history[camera_id][obj_id].append(position_data)
        if len(self.object_history[camera_id][obj_id]) > 30:
            self.object_history[camera_id][obj_id] = self.object_history[camera_id][obj_id][-30:]
    
    def _analyze_person_behavior(self, camera_id: str, obj_id: str, obj: Dict, config: Dict) -> List[Dict]:
        """Analyze person behavior"""
        violations = []
        current_time = time.time()
        
        # Check if person is in restricted area
        if config.get('enable_restricted_area', False):
            if self._is_in_restricted_area(obj, config.get('restricted_area', [])):
                violation_type = 'restricted_area_violation'
                violation_key = f"{camera_id}_{obj_id}_{violation_type}"
                
                # Check if we need to throttle violation reports
                if self._should_report_violation(violation_key, current_time, config.get('violation_cooldown', 30)):
                    violations.append({
                        'type': violation_type,
                        'object_id': obj_id,
                        'object_class': obj.get('class_name', 'unknown'),
                        'message': f"Person {obj_id} entered restricted area",
                        'timestamp': current_time
                    })
                    self._update_violation_info(violation_key, current_time)
        
        # Check loitering behavior
        if config.get('enable_loitering_detection', False):
            loiter_time = config.get('loitering_threshold', 30)
            if self._is_loitering(camera_id, obj_id, loiter_time):
                violation_type = 'loitering_violation'
                violation_key = f"{camera_id}_{obj_id}_{violation_type}"
                
                if self._should_report_violation(violation_key, current_time, config.get('violation_cooldown', 60)):
                    violations.append({
                        'type': violation_type,
                        'object_id': obj_id,
                        'object_class': obj.get('class_name', 'unknown'),
                        'message': f"Person {obj_id} loitering for more than {loiter_time} seconds",
                        'timestamp': current_time
                    })
                    self._update_violation_info(violation_key, current_time)
        
        return violations
    
    def _analyze_vehicle_behavior(self, camera_id: str, obj_id: str, obj: Dict, config: Dict) -> List[Dict]:
        """Analyze vehicle behavior"""
        violations = []
        current_time = time.time()
        
        # Check if vehicle is in restricted area
        if config.get('enable_restricted_area', False):
            if self._is_in_restricted_area(obj, config.get('restricted_area', [])):
                violation_type = 'vehicle_restricted_area_violation'
                violation_key = f"{camera_id}_{obj_id}_{violation_type}"
                
                if self._should_report_violation(violation_key, current_time, config.get('violation_cooldown', 30)):
                    violations.append({
                        'type': violation_type,
                        'object_id': obj_id,
                        'object_class': obj.get('class_name', 'unknown'),
                        'message': f"Vehicle {obj_id} entered restricted area",
                        'timestamp': current_time
                    })
                    self._update_violation_info(violation_key, current_time)
        
        # Check traffic rule violations (simplified)
        if config.get('enable_traffic_rules', False):
            # Simple traffic light violation detection (simulation)
            if config.get('traffic_light_state', 'green') == 'red':
                violation_type = 'red_light_violation'
                violation_key = f"{camera_id}_{obj_id}_{violation_type}"
                
                if self._should_report_violation(violation_key, current_time, config.get('violation_cooldown', 10)):
                    violations.append({
                        'type': violation_type,
                        'object_id': obj_id,
                        'object_class': obj.get('class_name', 'unknown'),
                        'message': f"Vehicle {obj_id} violated red light",
                        'timestamp': current_time
                    })
                    self._update_violation_info(violation_key, current_time)
        
        return violations
    
    def _analyze_generic_behavior(self, camera_id: str, obj_id: str, obj: Dict, config: Dict) -> List[Dict]:
        """Analyze behavior for other object types"""
        violations = []
        # Generic behavior analysis
        return violations
    
    def _is_in_restricted_area(self, obj: Dict, restricted_areas: List[List[int]]) -> bool:
        """Check if object is in restricted area"""
        # Get object center
        x_center = (obj.get('x1', 0) + obj.get('x2', 0)) / 2
        y_center = (obj.get('y1', 0) + obj.get('y2', 0)) / 2
        
        # Check against each restricted area
        for area in restricted_areas:
            if len(area) >= 4:
                ax1, ay1, ax2, ay2 = area[0], area[1], area[2], area[3]
                if ax1 <= x_center <= ax2 and ay1 <= y_center <= ay2:
                    return True
        
        return False
    
    def _is_loitering(self, camera_id: str, obj_id: str, threshold_seconds: float) -> bool:
        """Check if object is loitering"""
        if obj_id not in self.object_history[camera_id] or len(self.object_history[camera_id][obj_id]) < 10:
            return False
        
        # Get first and last position in history
        history = self.object_history[camera_id][obj_id]
        first_pos = history[0]
        last_pos = history[-1]
        
        # Calculate time difference
        time_diff = last_pos['timestamp'] - first_pos['timestamp']
        
        # Calculate position difference
        first_center = ((first_pos['x1'] + first_pos['x2']) / 2, (first_pos['y1'] + first_pos['y2']) / 2)
        last_center = ((last_pos['x1'] + last_pos['x2']) / 2, (last_pos['y1'] + last_pos['y2']) / 2)
        
        distance = np.sqrt((last_center[0] - first_center[0])**2 + (last_center[1] - first_center[1])** 2)
        
        # If time is long but distance is small, consider loitering
        return time_diff > threshold_seconds and distance < 50  # 50 pixels threshold
    
    def _should_report_violation(self, violation_key: str, current_time: float, cooldown: float) -> bool:
        """Check if violation should be reported (cooldown mechanism)"""
        if violation_key not in self.last_violation_time:
            return True
        
        return current_time - self.last_violation_time[violation_key] > cooldown
    
    def _update_violation_info(self, violation_key: str, current_time: float):
        """Update violation information"""
        self.last_violation_time[violation_key] = current_time
        
        if violation_key not in self.violation_counts:
            self.violation_counts[violation_key] = 0
        
        self.violation_counts[violation_key] += 1
    
    def _cleanup_old_data(self, camera_id: str):
        """Clean up old tracking data"""
        current_time = time.time()
        max_history_age = 300  # 5 minutes
        
        # Clean up object history
        if camera_id in self.object_history:
            to_remove = []
            for obj_id, history in self.object_history[camera_id].items():
                if history and current_time - history[-1]['timestamp'] > max_history_age:
                    to_remove.append(obj_id)
            
            for obj_id in to_remove:
                del self.object_history[camera_id][obj_id]
    
    def _get_default_config(self) -> Dict[str, Any]:
        """Get default configuration"""
        return {
            'enable_restricted_area': False,
            'restricted_area': [],
            'enable_loitering_detection': False,
            'loitering_threshold': 30,
            'enable_traffic_rules': False,
            'traffic_light_state': 'green',
            'violation_cooldown': 30
        }
    
    def get_statistics(self, camera_id: str) -> Dict[str, Any]:
        """Get analysis statistics for specific camera
        
        Args:
            camera_id: Camera identifier
            
        Returns:
            Dict containing statistics
        """
        stats = {
            'camera_id': camera_id,
            'tracked_objects_count': len(self.object_history.get(camera_id, {})),
            'violations_count': {},
            'last_violation': {}
        }
        
        # Group violations by type
        for violation_key, count in self.violation_counts.items():
            if violation_key.startswith(camera_id):
                parts = violation_key.split('_')
                if len(parts) >= 3:
                    violation_type = '_'.join(parts[2:])
                    if violation_type not in stats['violations_count']:
                        stats['violations_count'][violation_type] = 0
                    stats['violations_count'][violation_type] += count
        
        # Get last violation times
        for violation_key, timestamp in self.last_violation_time.items():
            if violation_key.startswith(camera_id):
                parts = violation_key.split('_')
                if len(parts) >= 3:
                    violation_type = '_'.join(parts[2:])
                    stats['last_violation'][violation_type] = timestamp
        
        return stats

# Create singleton instance for easy import
def get_behavior_analyzer() -> BehaviorAnalyzer:
    """Get behavior analyzer instance
    
    Returns:
        BehaviorAnalyzer instance
    """
    if not hasattr(get_behavior_analyzer, '_instance'):
        get_behavior_analyzer._instance = BehaviorAnalyzer()
    return get_behavior_analyzer._instance